package cn.hamster3.mc.plugin.core.bukkit.command.nbt;

import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.core.bukkit.util.CoreBukkitUtils;
import de.tr7zw.changeme.nbtapi.*;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class NBTCommand extends ParentCommand {
    public static final NBTCommand INSTANCE = new NBTCommand();
    private final Map<NBTType, String> NBT_TYPE_NAME;

    private NBTCommand() {
        NBT_TYPE_NAME = new HashMap<>();
        NBT_TYPE_NAME.put(NBTType.NBTTagEnd, "end");
        NBT_TYPE_NAME.put(NBTType.NBTTagByte, "byte");
        NBT_TYPE_NAME.put(NBTType.NBTTagShort, "short");
        NBT_TYPE_NAME.put(NBTType.NBTTagInt, "int");
        NBT_TYPE_NAME.put(NBTType.NBTTagLong, "long");
        NBT_TYPE_NAME.put(NBTType.NBTTagFloat, "float");
        NBT_TYPE_NAME.put(NBTType.NBTTagDouble, "double");
        NBT_TYPE_NAME.put(NBTType.NBTTagByteArray, "byte array");
        NBT_TYPE_NAME.put(NBTType.NBTTagString, "string");
        NBT_TYPE_NAME.put(NBTType.NBTTagList, "list");
        NBT_TYPE_NAME.put(NBTType.NBTTagCompound, "compound");
        NBT_TYPE_NAME.put(NBTType.NBTTagIntArray, "int array");
        NBT_TYPE_NAME.put(NBTType.NBTTagLongArray, "long array");
    }

    @Override
    public @NotNull JavaPlugin getPlugin() {
        return HamsterCorePlugin.getInstance();
    }

    @Override
    public @NotNull String getName() {
        return "hamster-nbt";
    }

    @Override
    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
        if (!(sender instanceof Player)) {
            CoreMessage.COMMAND_MUST_USED_BY_PLAYER.show(sender);
            return true;
        }
        Player player = (Player) sender;
        PlayerInventory inventory = player.getInventory();
        ItemStack stack = inventory.getItemInMainHand();
        if (CoreBukkitUtils.isEmptyItemStack(stack)) {
            sender.sendMessage("§c你的手持物品为空");
            return true;
        }
        NBTItem nbtItem = new NBTItem(stack);
        sendNBTCompound(nbtItem, sender, 0);
        return true;
    }

    @SuppressWarnings("ForLoopReplaceableByForEach")
    private void sendNBTCompound(NBTCompound compound, CommandSender sender, int level) {
        StringBuilder prefixBuilder = new StringBuilder();
        for (int i = 0; i < level; i++) {
            prefixBuilder.append("  ");
        }
        String space = prefixBuilder.toString();
        for (String key : compound.getKeys()) {
            NBTType type = compound.getType(key);
            switch (type) {
                case NBTTagByte: {
                    sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getByte(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagShort: {
                    sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getShort(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagInt: {
                    sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getInteger(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagLong: {
                    sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getLong(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagFloat: {
                    sender.sendMessage(String.format("%s- %s:%f §e§l[%s]", space, key, compound.getFloat(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagDouble: {
                    sender.sendMessage(String.format("%s- %s:%f §e§l[%s]", space, key, compound.getDouble(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagString: {
                    sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, compound.getString(key), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagCompound: {
                    sender.sendMessage(String.format("%s- %s §e§l[%s]", space, key, NBT_TYPE_NAME.get(type)));
                    sendNBTCompound(compound.getCompound(key), sender, level + 1);
                    break;
                }
                case NBTTagByteArray: {
                    byte[] array = compound.getByteArray(key);
                    sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagIntArray: {
                    int[] array = compound.getIntArray(key);
                    sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
                    if (array != null && array.length == 4) {
                        sender.sendMessage(String.format("%s- %s:%s §e§l[uuid]", space, key, compound.getUUID(key)));
                    }
                    break;
                }
                case NBTTagLongArray: {
                    long[] array = compound.getLongArray(key);
                    sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
                    break;
                }
                case NBTTagList: {
                    NBTType listType = compound.getListType(key);
                    if (listType == null) {
                        break;
                    }
                    sender.sendMessage(String.format("%s- %s §e§l[%s %s]", space, key, NBT_TYPE_NAME.get(type), NBT_TYPE_NAME.get(listType)));
                    String listSpace = space + "  ";
                    switch (listType) {
                        case NBTTagString: {
                            NBTList<String> list = compound.getStringList(key);
                            for (String string : list) {
                                sender.sendMessage(String.format("%s- %s", listSpace, string));
                            }
                            break;
                        }
                        case NBTTagIntArray: {
                            boolean all4Length = true;
                            NBTList<int[]> list = compound.getIntArrayList(key);
                            for (int[] intArray : list) {
                                if (intArray.length != 4) {
                                    all4Length = false;
                                    break;
                                }
                            }
                            if (all4Length) {
                                NBTList<UUID> uuidList = compound.getUUIDList(key);
                                for (int i = 0; i < list.size(); i++) {
                                    int[] intArray = list.get(i);
                                    UUID uuid = uuidList.get(i);
                                    sender.sendMessage(String.format("%s- %s", listSpace, Arrays.toString(intArray)));
                                    sender.sendMessage(String.format("%s- %s", listSpace, uuid));
                                }
                            } else {
                                for (int[] intArray : list) {
                                    sender.sendMessage(String.format("%s- %s", listSpace, Arrays.toString(intArray)));
                                }
                            }

                            break;
                        }
                        case NBTTagInt: {
                            NBTList<Integer> list = compound.getIntegerList(key);
                            for (Integer value : list) {
                                sender.sendMessage(String.format("%s- %d", listSpace, value));
                            }
                            break;
                        }
                        case NBTTagLong: {
                            NBTList<Long> list = compound.getLongList(key);
                            for (Long value : list) {
                                sender.sendMessage(String.format("%s- %d", listSpace, value));
                            }
                            break;
                        }
                        case NBTTagFloat: {
                            NBTList<Float> list = compound.getFloatList(key);
                            for (Float value : list) {
                                sender.sendMessage(String.format("%s- %f", listSpace, value));
                            }
                            break;
                        }
                        case NBTTagDouble: {
                            NBTList<Double> list = compound.getDoubleList(key);
                            for (Double value : list) {
                                sender.sendMessage(String.format("%s- %f", listSpace, value));
                            }
                            break;
                        }
                        case NBTTagCompound: {
                            NBTCompoundList compoundList = compound.getCompoundList(key);
                            for (int i = 0; i < compoundList.size(); i++) {
                                NBTListCompound listCompound = compoundList.get(i);
                                sendNBTCompound(listCompound, sender, level + 1);
                            }
                        }
                    }
                    break;
                }
                case NBTTagEnd: {
                    sender.sendMessage(String.format("%s- %s §e§l[%s]", space, key, NBT_TYPE_NAME.get(type)));
                    return;
                }
            }
        }
    }
}
